/**
 * 
 */
package truerefactor.graph;

import java.util.ArrayList;
import java.util.List;

/**
 * GraphPool - An ObjectPool implementation which is used to minimize the amount
 * of work the Java Garbage Collector has to accomplish. It was constructed in
 * order to minimize the memory foot print of the program. Prior to this change
 * the system was cloning graphs faster than the garbage collector could handle
 * and the amount of useless data in memory continued to pile up. This class now
 * handles cloning using a system similar to double buffering in graphics
 * programming. We create a minimum of three copies of the graph in the pool
 * with a separate pristine cloning graph outside the pool. Everytime a fresh
 * copy is needed, the next graph on deck is returned, and the one after it is
 * cleared and cloned into from the pristine copy. Note that this is not a
 * thread safe operation. If one thread is still using the graph which is being
 * cleared and cloned into there could be a problem with reading data that
 * wasn't there before. In order to get around the threading issue a type of
 * locking system for the code graph will be needed or a means to release the
 * graph from the pool without it being replaced. if the pool is emptied, the
 * caller will be forced to sit and wait until a graph has been cleared and
 * re-cloned to pristine condition and then reinserted into the pool. Only then
 * can the graph be reused. Once this occurs this class will be thread safe.
 * 
 * @author Isaac
 */
public class GraphPool {

    /**
     * the singleton instance of this graph pool
     */
    private static GraphPool instance;
    /**
     * current index of the pool
     */
    private int currentIndex = 0;
    /**
     * pool of CodeGraph objects
     */
    private List<CodeGraph> graphs;
    /**
     * minimum size of the graph pool
     */
    private int minSize = 3;
    /**
     * maximum size of the graph pool
     */
    private int maxSize = minSize;
    /**
     * the prisine copy of the initial code graph from which clones are made
     */
    private CodeGraph pristine;

    /**
     * Creates a new GraphPool instance
     */
    private GraphPool()
    {
        pristine = new CodeGraph();
        graphs = new ArrayList<CodeGraph>(maxSize);
        for (int i = 0; i < maxSize; i++)
        {
            graphs.add(new CodeGraph());
        }
    }

    /**
     * @return the singleton instance of GraphPool
     */
    public static GraphPool getInstance()
    {
        if (instance == null)
        {
            instance = new GraphPool();
        }

        return instance;
    }

    /**
     * @return the next available CodeGraph from the pool
     */
    public CodeGraph acquireCodeGraph()
    {
        CodeGraph retVal = graphs.get(currentIndex % maxSize);
        CodeGraph onDeck = graphs.get((currentIndex + 1) % maxSize);

        try
        {
            onDeck.clear();
            pristine.cloneInto(onDeck);
        }
        catch (CloneNotSupportedException cnsx)
        {
            cnsx.printStackTrace();
        }

        currentIndex++;

        return retVal;
    }

    /**
     * Removes the provided graph from the graph pool and replaces it with a new
     * instance.
     * 
     * @param graph
     *            graph to be removed and replaced in the pool.
     */
    public void releaseCodeGraph(CodeGraph graph)
    {
        int index = graphs.indexOf(graph);
        graphs.add(index, new CodeGraph());
        graphs.remove(graph);
    }

    /**
     * Sets the new maximum size of the pool to be the provided value
     * 
     * @param maxSize
     *            new maximum size of the pool
     */
    public void setMaxPoolSize(int maxSize)
    {
        this.maxSize = maxSize;
        if (maxSize < minSize)
            this.maxSize = minSize;

        if (graphs.size() > maxSize)
        {
            int difference = graphs.size() - maxSize;
            for (int i = difference; i > 0; i--)
            {
                graphs.remove(graphs.size() - 1);
            }
        }
    }

    /**
     * Sets the initial ("pristine") graph to be the one provided. This is the
     * graph from which all clones will be made.
     * 
     * @param graph
     *            initial CodeGraph to be used for cloning
     */
    public void setInitialGraph(CodeGraph graph)
    {
        graphs.clear();
        graphs.add(graph);

        try
        {
            graph.cloneInto(pristine);

            for (int i = 0; i <= maxSize - 2; i++)
            {
                CodeGraph onDeck = new CodeGraph();
                graphs.get(i).cloneInto(onDeck);
                graphs.add(onDeck);
            }
        }
        catch (CloneNotSupportedException cnsx)
        {
            cnsx.printStackTrace();
        }
    }
}
